package com.cskaoyan.javase.object._5clone;

/**
 * protected native Object clone() throws CloneNotSupportedException;
 * clone，就是克隆。Java当中的克隆方法`clone()`的作用：
 * 1. 得到一个完全独立的对象。
 * 2. 成员仍和原先对象一致。
 * 这是Object类当中的默认实现
 *
 * 使用克隆方法的步骤：
 * 1.需要解决方法的权限问题。应该如果权限不解决，那么就只能自己克隆自己，这在多数情况下意义不大，我们需要在类的外部
 *      去克隆一个类的对象。
 * 2.可以选择重写方法的返回值类型为当前对象的类型（当然也可以不做，不是必须的，但是推荐做）
 *
 * 上述步骤完成后，直接调用clone方法会抛出异常CloneNotSupportedException，而且程序终止了。
 * 在Java中，要想对某个类的对象做克隆操作，必须实现接口java.lang.Cloneable，表示该类支持克隆操作。如果一个类没有实现该接口，然后对象又做克隆操作，就会抛出异常
 *CloneNotSupportedException。
 *
 * 3.让某个去类实现Cloneable接口，然后再做克隆操作。
 *
 * 以上基本的克隆操作就完成了。
 *
 * 克隆操作中的细节问题：
 * 1.Cloneable接口是一个空接口，里面没有任何成员，没有任何代码，是一个空的接口。
 *      实现一个空接口有什么用呢？
 *      一个类实现某个接口，虽然从表面上看。该类没有新增任何成员。但是这个类的数据类型无形中就发生了变化
 *      ---> 一旦实现某个接口，这个接口就成为父接口了（父类）
 *      ---> 引用 instanceof 类名/接口名
 *      通过判断，确定某个对象是否实现该接口，如果实现了就允许做克隆操作，如果没实现就抛出异常，禁止做克隆操作
 *      clone方法的底层逻辑就是这样的。
 *  像这种空接口，实现他不会增加任何成员，但一旦实现就可以做一些操作的接口，我们称之为"标记接口"
 *
 *  2.克隆创建对象是一种完全独立的，和new对象平行的创建对象方式。它依赖于本地方法实现，它不会调用构造器。
 *
 *
 *  3.Object类当中默认clone方法实现，会完全拷贝一份成员变量到新的对象当中：
 *      a.如果成员变量是基本数据类型,那不会有问题,因为拷贝的是值
 *      b.如果成员变量是引用数据类型,拷贝的是引用
 *      这样做的结果是: 拷贝后的引用和原先的引用指向同一对象
 *      那么它们的操作就会互相影响
 *      这样两个对象其实不是完全独立的
 *
 * 像Object当中默认实现的clone方法,只做了一层拷贝,只拷贝引用不拷贝对象,称之为"浅拷贝"
 * 那么如果我们重写clone方法,连引用指向的对象一起拷贝,就称之为"深拷贝 深克隆"
 *
 * 深度拷贝的步骤:
 * 1.考虑克隆了几个对象,克隆几个,那几个类都需要解决方法的权限问题,还需要实现接口Cloneable
 * 2.需要对哪个类的对象做深度克隆,就需要改写它的克隆方法.
 *      1.深克隆是在浅克隆的基础进行的
 *      2.把成员变量引用指向的对象克隆一份
 *      3.把拷贝引用指向拷贝后的新对象
 * 以上完成深度克隆
 *
 * @since 16:28
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        /*Student s = new Student(1, 18, 100);
        // 克隆s对象
        // Unhandled exception: java.lang.CloneNotSupportedException
        Student cloneStu = s.clone();
        System.out.println(cloneStu == s); //false
        System.out.println(cloneStu.equals(s)); //true

        s.score = 250;
        System.out.println(cloneStu.score);*/

        Student stu = new Student(1, 10, 80, new Dog("土狗"));
        Student cloneStu = stu.clone();
        System.out.println(cloneStu == stu);
        System.out.println(cloneStu.equals(stu));

        stu.d.breed = "金毛";
        System.out.println(cloneStu.d.breed);

    }
}

class Student implements MyCloneable {
    int id;
    int age;
    double score;
    Dog d;

    public Student() {
    }

    public Student(int id, int age, double score, Dog d) {
        this.id = id;
        this.age = age;
        this.score = score;
        this.d = d;
    }

    public Student(int id, int age, double score) {
        this.id = id;
        this.age = age;
        this.score = score;

        System.out.println("hello world!");
    }

    /*
        重写clone方法的访问权限（必做的）
        再重写返回值类型（选做）
     */
    @Override
    protected Student clone() throws CloneNotSupportedException {
        // 1.深克隆是在浅克隆的基础进行的
        Student cloneStu = (Student) super.clone();
        // 2.把成员变量引用指向的对象克隆一份
        Dog cloneDog= this.d.clone();
        // 3.把拷贝引用指向拷贝后的新对象
        cloneStu.d = cloneDog;
        return cloneStu;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (id != student.id) return false;
        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return d != null ? d.equals(student.d) : student.d == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (d != null ? d.hashCode() : 0);
        return result;
    }
}

class Dog implements MyCloneable {
    String breed;

    public Dog() {
    }

    public Dog(String breed) {
        this.breed = breed;
    }

    @Override
    protected Dog clone() throws CloneNotSupportedException {
        return ((Dog) super.clone());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Dog dog = (Dog) o;

        return breed != null ? breed.equals(dog.breed) : dog.breed == null;
    }

    @Override
    public int hashCode() {
        return breed != null ? breed.hashCode() : 0;
    }
}

interface MyCloneable extends java.lang.Cloneable {
}